/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package aescfbencryption.keys;

import aescfbencryption.basicOperations.basicOperations;
import java.util.Vector;

/**
 *
 * @author Karim
 */
public class KeyGen {

    String keyWord;
    Vector asciiKeyWord;
    Vector hexKeyWord;
    Vector wN;
    int[][] keyMatrix;
    int columnas;
    staticMatrix s;

    /** key= new Keygen(String,columnas)
         *  columnas: 4 (128bits)
         *  columnas: 6 (192bits)
         *  columnas: 8 (256bits)
         */
    public KeyGen(String palabraClave, int columnas) {
        super();
        this.keyWord = palabraClave;
        this.asciiKeyWord = new Vector();
        this.hexKeyWord = new Vector();
        this.keyMatrix = new int[4][columnas];
        this.wN = new Vector();//vector para guardar los w(N)
        this.columnas = columnas;
        this.s = new staticMatrix();

    }

    public staticMatrix getS() {
        return s;
    }

    public void setS(staticMatrix s) {
        this.s = s;
    }



    public String getPalabraClave() {
        return keyWord;
    }

    public void setPalabraClave(String palabraClave) {
        this.keyWord = palabraClave;
    }

    public Vector getAsciiKeyWord() {
        return asciiKeyWord;
    }

    public void setAsciiKeyWord(Vector asciiKeyWord) {
        this.asciiKeyWord = asciiKeyWord;
    }

    public Vector getHexKeyWord() {
        return hexKeyWord;
    }

    public void setHexKeyWord(Vector hexKeyWord) {
        this.hexKeyWord = hexKeyWord;
    }

    public int[][] getKeyMatrix() {
        return keyMatrix;
    }

    public void setKeyMatrix(int[][] keyMatrix) {
        this.keyMatrix = keyMatrix;
    }

    public int getColumnas() {
        return columnas;
    }

    public void setColumnas(int columnas) {
        this.columnas = columnas;
    }

    public Vector getwN() {
        return wN;
    }

    public void setwN(Vector wN) {
        this.wN = wN;
    }

    
    /** Codifca el String de entrada en vectores de ascii y hexadecimales */
    public void codKeyWord()
    {   /** Codifca el String de entrada en vectores de ascii y hexadecimales */
        for(int i=0;i<this.getPalabraClave().length();i++)
        {
            this.getAsciiKeyWord().add((int)this.getPalabraClave().charAt(i));
            this.getHexKeyWord().add(Integer.toHexString((int)this.getPalabraClave().charAt(i)));
        }
    }


    /** Llena la matriz de la llave */
    public void fillKeyMatrix()
    {
        
        int e=0;
        for(int i=0;i<4;i++)
            for(int j=0;j<this.getColumnas();j++)
                if(e<this.getAsciiKeyWord().size())
                this.getKeyMatrix()[i][j]=Integer.parseInt(String.valueOf(this.getAsciiKeyWord().get(e++)));
                else this.getKeyMatrix()[i][j]=120;
                //System.out.println(i+","+j+" :"+e+" ::"+this.getKeyMatrix()[i][j]);
                //System.out.println(Integer.parseInt(String.valueOf(this.getHexKeyWord().get(++e)),16));
                //FINCIONA
    }

    /** Funcion que llena los primeros 4 vectores W(N)*/
    public void initFillWN()
    {
        this.fillKeyMatrix();
        for(int i=0;i<4;i++)//los 4 wN iniciales.
        {
            Vector temporal = new Vector();
            for(int j=0;j<this.getColumnas();j++)
                temporal.add(Integer.toHexString(this.getKeyMatrix()[i][j]));
            this.getwN().add(new Vector(temporal));

        }
        //System.out.println(this.getwN()); FUNCIONA
    }

    public Vector toHexValue(Vector datos)
    {
        Vector HexValues=new Vector();
        for(int i=0;i<datos.size();i++)
            HexValues.add(Integer.toHexString(Integer.valueOf(String.valueOf(datos.get(i)))));
        return HexValues;
    }

    public void fillAllWN()
    {
        // en este punto ya tengo los 4 vectores WN originales
        for(int i=4;i<44;i++)
        {
            Vector Wntemp = new Vector((Vector)this.getwN().get(i-1));

            System.out.println("iteracion:"+i+" temporal W"+(i-1)+": "+Wntemp);
            if(i%4==0)
            {
                System.out.println("WN Original"+Wntemp);
                Wntemp = this.RotByte(Wntemp);//Aplicamos RotByte
                System.out.println("WN RotByte"+Wntemp);
                Vector RCONi = new Vector(this.getRconAt(Wntemp,(i/4)-1));//Vector RCON
                System.out.println("RCON["+((i/4)-1)+"]: "+RCONi);
                Wntemp = rotByteToSubByte(Wntemp);
                System.out.println("WN SubByte"+Wntemp);
                Wntemp = arrayXOR(Wntemp,RCONi);
                System.out.println("Wntemp XOR RCONi: "+Wntemp);
            }
          System.out.println("Wntemp "+i+": "+Wntemp);
          this.getwN().add(arrayXOR((Vector)this.getwN().get(i-4),Wntemp));
        }
    }


    public int hexToInt(Object o)
    {
        String string= String.valueOf(o);
        return Integer.parseInt(string, 16);
    }

    
    public int toInt(Object o)
    {
        return Integer.valueOf(String.valueOf(o));
    }

    /** v1.size() = v2.size() */
    public Vector arrayXOR(Vector v1, Vector v2)
    {
        Vector tmp = new Vector();
        for(int i=0;i<v1.size();i++)
            tmp.add(Integer.toHexString(hexToInt(v1.get(i))^hexToInt(v2.get(i))));
        return tmp;
    }

    public Vector RotByte(Vector wN)
    {
        Vector temporal = new Vector();
        for(int i=1;i<wN.size();i++)
            temporal.add(wN.get(i));
            temporal.add(wN.get(0));
        return temporal;
    }

    public int valueOfHex(char hex)
    {
        switch(hex)
        {
            case '1':return 1;case '2':return 2;case '3':return 3;
            case '4':return 4;case '5':return 5;case '6':return 6;
            case '7':return 7;case '8':return 8;case '9':return 9;
            case 'a':return 10;case 'b':return 11;case 'c':return 12;
            case 'd':return 13;case 'e':return 14;case 'f':return 15;
            case '0':return 0;default:return 0;
        }
    }

    /** Este ByteSub recive un String hexadecimal y lo convierte a un entero 
     *  de la matriz S
     * */
    public int ByteSubAtomic(String valorHx)
    {
        int i = valueOfHex(valorHx.charAt(0));
        int j = valueOfHex(valorHx.charAt(1));
        return s.getSBoxAt(i,j);
    }

    public Vector ByteSub(Vector HexKeyWord)
    {
        Vector salidaSubByte = new Vector();
        for(int i=0;i<HexKeyWord.size();i++)
         salidaSubByte.add(ByteSubAtomic(String.valueOf(HexKeyWord.get(i))));
        return salidaSubByte;
    }

    public Vector getRconAt(Vector hex,int k)
    {
        Vector tmp = new Vector();
        for(int i=0;i<hex.size();i++)
            if(i==0)
                tmp.add(Integer.toHexString(this.getS().RCon[k]));//aca estaba el error.
            else
                tmp.add(0x00);
        return tmp;
        
    }

    /** Entrega una matriz de 4xColumna que representa a la llave N*/
    public int[][] getW(int n)
    {
        int[][] matriz = new int[4][this.getColumnas()];
        /** Recorriendo filas*/
        for(int i=0;i<4;i++)
            for(int j=0;j<this.getColumnas();j++)
                matriz[i][j]=  hexToInt(((Vector)this.getwN().get(i+(4*n))).get(j));
        return matriz;
     }

    @SuppressWarnings("static-access")
    public Vector rotByteToSubByte(Vector WnTemp)
    {
        basicOperations b = new basicOperations();
        Vector salida = new Vector();
        for(int i=0;i<WnTemp.size();i++)
            salida.add(Integer.toHexString(b.getsAES()[hexToInt(WnTemp.get(i))]));
        return salida;
    }


}
